<template>
<div class="uploader">
  <div>
    <el-dropdown>
      <el-button size="small" type="primary">
        选择上传<i class="el-icon-arrow-down el-icon--right"></i>
      </el-button>
      <el-dropdown-menu slot="dropdown">
        <el-dropdown-item id="uploadFileBtn">选择文件</el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
    <el-button size="small" type="primary" @click="onUpload">上传</el-button>
  </div>
  <div class="file-list">
    <div class="upload-file-item" v-for="(item, index) in uploadFiles" :key="index">
      <el-row>
        <el-col>
              <u-progress :name="item.oname" :status="item.uploadStatus" :idx="index" v-on:redoc2png="createPdf2PngTask(index)" :tip="item.tip" :progress="item.uploadProgress"></u-progress>
        </el-col>
      </el-row>
    </div>
  </div>
</div>
</template>
<script>
// 引入依赖库
import uploader from 'uploader'
import { Base64 } from 'js-base64'
import Crypto from '@/utils/crypto/crypto'
import '@/utils/crypto/hmac'
import '@/utils/crypto/sha1'

// 引入组件
import UploadProgress from './uploadProgress'

// 引入API
import { pdf2png, checkTask, setHeader } from '@/api/report'
import { clearInterval, setInterval } from 'timers';

// 策略
var policyText = {
    "expiration": "2030-01-01T12:00:00.000Z", //设置该Policy的失效时间，超过这个失效时间之后，就没有办法通过这个policy上传文件了
    "conditions": [
      ["content-length-range", 0, 1048576000]   // 设置上传文件的大小限制
    ]
}

const oss_endpoint = 'https://seedsufe-plan.oss-cn-shenzhen.aliyuncs.com'
// 组件
export default {
  name: 'OssUploader',
  data() {
    return {
      reset: 0,
      uploadFiles: [],
      uploader: undefined,
      oss: {
        accessid: 'LTAIMBcXnd6ielEF',
        accesskey: 'nAbZ4q74zx4yePoIQ8GDWPuYcehWoI',
        policyBase64: '',
        signature: '',
      },
      lastFileId: 0,
      options: {
        // target: "http://sufeseed-tmp.oss-cn-beijing.aliyuncs.com",
        target: oss_endpoint,
        chunkSize: 1024 * 1024 * 200,
        testChunks: false,
        simultaneousUploads: 10,
        query: this.makeUploadQuery,
      },
    }
  },
  watch: {
    reset(nv, ov) {
      this.initComp();
    }
  },
  props: ['pid'],
  components: {
    'u-progress': UploadProgress
  },
  mounted() {
    this.initUploader()
  },
  methods: {
    // 提交数据到父组件中
    file2pd() {
      let submitFiles = [];
      /**
       ** uploadFiles - subitem data construct
       * taskId: 0,                          // 上传任务ID
       * pageCount: 0,                       // 总页数
       * pid: file.pid,                      // files中索引的pid
       * name: "",                           // oss存储的文件名
       * oname:file.name,                    // 原始文件名 origin name
       * relativePath: file.relativePath,    // 实际路径
       * pdfUrl: '',                         // PDF阅读文件夹
       * pageDir: '',                        // 在线阅读文件夹
       * tip: '已添加未上传',                  // 提示信息
       * uploadProgress: 0,                 // 上传进度
       * transProgress: 0,                  // 转换进度
       * uploadStatus: 0,                   // 上传状态, 0: 为上传, 1: 上传中, 2: 上传完成
       * qrcode: '',                        // 二维码地址
       * id: 0,
       *
       */
      this.uploadFiles.forEach(uploadFile => {
        let subFile = {
          'save_name': uploadFile.relativePath,
          'online_url': uploadFile.name,
          'sign_ts': uploadFile.fid,
          'pages': uploadFile.pageDir,
          'file_id':0,
          'page_count': uploadFile.pageCount
        }
        submitFiles.push(subFile)
      })
      this.$emit('file2pd', submitFiles)
    },
    // 初始化组件
    initComp() {
      console.log("reset oss uploader")
      this.uploadFiles = [];
    },
    // 初始化Uploader
    initUploader() {
      // 初始化OSS数据
      this.initOss()
      // 初始化上传年组件
      this.uploader = new uploader(this.options)
      // 选择触发事件的按钮
      var selectFile = document.getElementById('uploadFileBtn');
      // 将浏览器DOM赋给uploader组件监听,触发不同的上传方式
      this.uploader.assignBrowse(selectFile, false, false, {
        'accept': '.pdf,.ppt,.pptx'
      })
      // 监听事件, 单文件上传成功
      this.uploader.on('fileSuccess', this.fileSuccessHandler)
      // 监听事件, 上传完成
      this.uploader.on('complete', this.compHandler)
      // 监听事件, 添加文件
      this.uploader.on('fileAdded', this.fileAddedHandler)
      // 监听事件, 文件上传进度
      this.uploader.on('fileProgress', this.fileProgressHandler)
    },
    // 初始化OSS
    initOss() {
      this.oss.policyBase64 = Base64.encode(JSON.stringify(policyText))
      let bytes = Crypto.HMAC(Crypto.SHA1, this.oss.policyBase64, this.oss.accesskey, { asBytes: true })
      this.oss.signature = Crypto.util.bytesToBase64(bytes)
    },
    // 上传成功
    compHandler(file, event) {
      this.file2pd()
    },
    // 上传完成
    fileSuccessHandler(rootFile, file, message, chunk) {
      let upIdx = this.findFileIdxByName(file.relativePath)

      if (upIdx>=0 && upIdx < this.uploadFiles.length) {
        this.uploadFiles[upIdx].uploadProgress = 100
        this.uploadFiles[upIdx].uploadStatus = 2
        this.uploadFiles[upIdx].tip = '上传完成'

        // 修改文件Header
        setHeader(this.uploadFiles[upIdx].name, file.name, file.fileType)
        // 添加到文件列表中(第一次, 防备转化失败的情况)
        this.file2pd()
        // 创建拆分任务
        this.createPdf2PngTask(upIdx)
      }
    },
    // 添加文件
    fileAddedHandler(file, fileList, event) {
      let pass = this.checkUploadFile(file)
      if (pass) {
        this.uploadFiles.push({
            fid: 0,                             // 保存文件时间戳(oss中的文件名)
            taskId: 0,                          // 上传任务ID
            pageCount: 0,                       // 总页数
            pid: file.pid,                      // files中索引的pid
            name: "",                           // oss存储的文件名
            oname:file.name,                    // 原始文件名 origin name
            relativePath: file.relativePath,    // 实际路径
            pdfUrl: '',                         // PDF阅读文件夹
            pageDir: '',                        // 在线阅读文件夹
            tip: '已添加未上传',                  // 提示信息
            uploadProgress: 0,                  // 上传进度
            transProgress: 0,                   // 转换进度
            uploadStatus: 0,                    // 上传状态, 0: 为上传, 1: 上传中, 2: 上传完成
            id: 0,
          }
        )
      }
      // 检查添加的文件
      return pass
    },
    // 文件上传进度
    fileProgressHandler(rootFile, file, chunk) {
      // 获取上传文件
      let upIdx = this.findFileIdxByName(file.relativePath)
      if (upIdx >= 0 && upIdx < this.uploadFiles.length) {
        this.uploadFiles[upIdx].uploadProgress = Math.floor(file.uploader.progress() * 100) ;
        this.uploadFiles[upIdx].step += 1;
      }
    },
    // 检查上传的文件
    checkUploadFile(file) {
      file.pid = file.id + this.lastFileId
      if (file.relativePath.split("/").length > 3) {
          // 移除不符合规范的文件, 此处最多只添加2层
          return false
      }
      // 检查文件名是否是以"."开头
      if (file.name.substr(0, 1) === ".") {
        return false
      }
      // 检查是否是合法类型的文件
      // if (lawUploadFileTypes.indexOf(file.fileType) == -1) {
      //     return false
      // }
      return true
    },
    // 初始化文件列表
    insertFileRecord(file) {

    },
    // 上传文件、文件夹
    onUpload() {
      this.uploadFiles.forEach(uploadFile => {
        uploadFile.uploadStatus = 1
        uploadFile.tip = '开始上传'
      })
      // 上传
      this.uploader.upload()
    },
    // 刷新文件的名称
    makeOssFileName(file) {
      // 文件名称
      let filename = file.name;
      let suffix = this.getSuffix(filename);
      let fid = this.timestamp()
      let ossDir = this.getOssDir() + "doc_" + this.uuid()
      let pageDir = "pages/" + ossDir
      let ossFileName =  ossDir + suffix;
      // 更新oss存储文件名
      let ufIdx = this.findFileIdxByName(file.relativePath)
      if (ufIdx >= 0) {
        this.uploadFiles[ufIdx].fid = fid
        this.uploadFiles[ufIdx].name = ossFileName
        this.uploadFiles[ufIdx].pageDir = pageDir
        this.uploadFiles[ufIdx].pdfUrl = oss_endpoint + "/" + ossFileName
      }
      return ossFileName
    },
    // 根据文件名找到文件的索引ID
    findFileIdxByName(name) {
      let idx = -1;
      for (let i = 0; i < this.uploadFiles.length; i++) {
        if (this.uploadFiles[i].relativePath == name) {
          idx = i;
          break;
        }
      }
      return idx;
    },
    // 加密HMAC
    hmac(message, key, options) {
      // Allow arbitrary length keys
      key = key.length > hasher._blocksize * 4 ?
            hasher(key, { asBytes: true }) :
            util.stringToBytes(key);

      // XOR keys with pad constants
      var okey = key,
          ikey = key.slice(0);
      for (var i = 0; i < hasher._blocksize * 4; i++) {
        okey[i] ^= 0x5C;
        ikey[i] ^= 0x36;
      }
      var hmacbytes = hasher(util.bytesToString(okey) +
	                       hasher(util.bytesToString(ikey) + message, { asString: true }),
	                       { asBytes: true });
      return options && options.asBytes ? hmacbytes :
            options && options.asString ? util.bytesToString(hmacbytes):
            util.bytesToHex(hmacbytes);
    },
    // 获取时间戳
    timestamp() {
      let d = new Date()
      return d.getTime() * 1000
    },
    // uuid
    uuid() {
      var s = [];
      var hexDigits = "0123456789ABCEDF";
      for (var i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
      }
      s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
      s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
      s[8] = s[13] = s[18] = s[23] = "";

      var uuid = s.join("");
      return uuid;
    },
    random_string(len) {
  　　len = len || 32;
  　　var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
  　　var maxPos = chars.length;
  　　var pwd = '';
  　　for (let i = 0; i < len; i++) {
      　　pwd += chars.charAt(Math.floor(Math.random() * maxPos));
      }
      return pwd;
    },
    // 参数
    makeUploadQuery(file) {
      let ossFileName = this.makeOssFileName(file)
      return {
        'key' : ossFileName,
        'policy': this.oss.policyBase64,
        'OSSAccessKeyId': this.oss.accessid,
        'success_action_status' : '200', //让服务端返回200,不然，默认会返回204
        'signature': this.oss.signature
      }
    },
    // 获取路径
    getOssDir() {
      let date = new Date()
      let y = date.getFullYear()
      let m = date.getMonth()+1
      let d = date.getDate()
      if (m < 10) {
        m = '0' + m
      }
      if (d < 10) {
        d = '0' + d
      }
      return y+"/"+m+"/"+d+"/"
    },
    getSuffix(filename) {
      let pos = filename.lastIndexOf(".")
      let suffix = ''
      if (pos != -1) {
          suffix = filename.substring(pos)
      }
      return suffix;
    },
    // 非任务, 直接从OSS获取文件夹内的文件数量
    createPdf2PngTask(idx) {
      let fid = this.uploadFiles[idx].fid
      let pageDir = this.uploadFiles[idx].pageDir
      this.uploadFiles[idx].tip = '开始文档转换...'
      let ossName = this.uploadFiles[idx].name
      // 开始执行拆分操作
      pdf2png(ossName, pageDir + "/").then(res => {
        // 启动定时任务, 检查
        this.uploadFiles[idx].transProgress = 0
        this.uploadFiles[idx].taskId = res.task_id
        this.uploadFiles[idx].pageCount = res.totalPage
      })
      // 开启检查
      this.check2png(idx)
    },
    // 检查是否已经完成分解
    check2png(idx) {
      // 获取sid
      this.uploadFiles[idx].loopId = setInterval(() => {
        // 验证文件是否拆分完成
        if (this.uploadFiles[idx].taskId) {
          checkTask(this.uploadFiles[idx].taskId).then(res => {
            this.uploadFiles[idx].transProgress = res.percent
            switch (res.status) {
              case 'Finished':
                this.checkTaskFin(idx, 1);
                break;
              case 'Failed':
                this.checkTaskFin(idx, 0);
                break;
            }
          })
        }
      }, 1000)
    },
    checkTaskFin(idx, status) {
      if (status == 1) {
        this.uploadFiles[idx].tip = '文档转换成功!'
        this.$message.success('文档<< '+this.uploadFiles[idx].oname+" >>转换成功 !!!");
      } else {
        this.uploadFiles[idx].tip = '文档转换失败!'
        this.$message.error('文档<< '+this.uploadFiles[idx].oname+" >>转换失败, 请重新分解 !!!");
      }
      // 一切都结束了
      clearInterval(this.uploadFiles[idx].loopId);
      // 添加到文件列表中
      this.file2pd()
    }
  }
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
  // 上传
  .uploader {
    width: 380px;
    min-height: 150px;
    background: #FCFCFC;
    padding: 10px;
    margin: 0px auto 0;
    border: 1px solid #DCDFE6;
    border-radius: 3px;
    font-size: 12px;
  }
  // 文件上传项目
  .upload-file-item {
    margin-top: 16px;
    .file-label {
      font-size: 14px;
      color: #333333;
      padding-bottom: 10px;
    }
    .file-list {
      border-top:1px dashed #CCC;
      margin-top: 16px;
      max-height: 300px;
      overflow-y:auto;
    }
  }
</style>

